﻿//Image PreProcessing
namespace Compression.ImageCompressor
{
    using System;
    using System.Drawing;

    public static class PreProcessing
    {
        private static int OffsetSize = 128;

        /// <summary>
        /// Tiling image on segmants
        /// </summary>
        /// <param name="img"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public static void Tiling(ref Bitmap img, int size, out Bitmap[] res)
        {
            int rows;
            int cols;
            int all;
            GetCountTiles(img, size, out rows, out cols, out all);
            res  = new Bitmap[all];            
            int k = 0;
            for (int i = 0; i < rows; i++)
            {
                int y = i * size;
                for (int j = 0; j < cols; j++)
                {
                    int x = j * size;
                    res[k] = new Bitmap(size,size);
                    for (int yy = 0; yy < size; yy++)
                    {
                        if (y + yy >= img.Height) continue;
                        for (int xx = 0; xx < size; xx++)
                        {
                            if (x + xx >= img.Width) continue;
                            res[k].SetPixel(xx, yy, img.GetPixel(x + xx, y + yy));
                        }
                    }
                    k++;
                }
            }            
        }

        public static void GetCountTiles(Image img, int size, out int rows, out int cols, out int all)
        {
            rows = img.Height / size;
            if (img.Height % size > 0) rows++;
            cols = img.Width / size;
            if (img.Width % size > 0) cols++;
            all = rows * cols;
        }

        /// <summary>
        /// Get One Tile from Bitmap
        /// </summary>
        /// <param name="img"></param>
        /// <param name="size"></param>
        /// <param name="numtile"></param>
        /// <param name="R"></param>
        /// <param name="G"></param>
        /// <param name="B"></param>
        public static void GetTile(Bitmap img, int size,int numtile, ref float[][] R, ref float[][] G, ref float[][] B)
        {
            int rows = img.Height / size;
            if (img.Height % size > 0) rows++;
            int cols = img.Width / size;
            if (img.Width % size > 0) cols++;
            int all = rows * cols;
            
            int k = 0;
            for (int i = 0; i < rows; i++)
            {
                int y = i * size;
                for (int j = 0; j < cols; j++)
                {
                    int x = j * size;
                    if (k == numtile)
                    {
                        for (int yy = 0; yy < size; yy++)
                        {
                            if (y + yy >= img.Height) continue;
                            for (int xx = 0; xx < size; xx++)
                            {
                                if (x + xx >= img.Width) continue;
                                //res[k].SetPixel(xx, yy, img.GetPixel(x + xx, y + yy));
                                Color clr = img.GetPixel(x + xx, y + yy);
                                R[xx][yy] = clr.R;
                                G[xx][yy] = clr.G;
                                B[xx][yy] = clr.B;
                                
                            }
                        }
                    }
                    k++;
                }
            }
        }

        /// <summary>
        /// Compiling Image from tiles
        /// </summary>
        /// <param name="img"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        public static void Compile(ref Bitmap[] img, int width, int height,out Bitmap res)
        {
            res = new Bitmap(width, height);
            int k = 0;            
            int size = img[0].Width;
            int curh = 0;
            while (curh < height)
            {
                int curw = 0;
                while (curw < width)
                {
                    for (int i = 0; i < size; i++)
                    {
                        if (curh + i >= height) continue;
                        for (int j = 0; j < size; j++)
                        {
                            if (curw + j >= width) continue;
                            res.SetPixel(curw + j, curh + i, img[k].GetPixel(j, i));
                        }
                    }
                    curw += size;
                    k++;
                }
                curh += size;
            }            
        }

        /// <summary>
        /// Set one time into bitmap
        /// </summary>
        /// <param name="res"></param>
        /// <param name="R"></param>
        /// <param name="G"></param>
        /// <param name="B"></param>
        /// <param name="size"></param>
        /// <param name="numtile"></param>
        public static void SetTile(ref Bitmap res, ref float[][] R, ref float[][] G, ref float[][] B,int size,int numtile)
        {
            //res = new Bitmap(width, height);
            int k = 0;
            //int size = img[0].Width;
            int curh = 0;
            while (curh < res.Height)
            {
                int curw = 0;
                while (curw < res.Width)
                {
                    if (k == numtile)
                    {
                        for (int i = 0; i < size; i++)
                        {
                            if (curh + i >= res.Height) continue;
                            for (int j = 0; j < size; j++)
                            {
                                if (curw + j >= res.Width) continue;
                                int rr=(int)R[j][i];
                                int gg=(int)G[j][i];
                                int bb=(int)B[j][i];
                                rr = Math.Max(0,Math.Min(255,rr));
                                gg = Math.Max(0,Math.Min(255,gg));
                                bb = Math.Max(0,Math.Min(255,bb));
                                res.SetPixel(curw + j, curh + i,Color.FromArgb(rr,gg,bb));
                            }
                        }
                    }
                    curw += size;
                    k++;
                }
                curh += size;
            }    
        }

        /// <summary>
        /// Forward and Reverse algorithm of bit offset
        /// </summary>
        /// <param name="matrix"></param>
        /// <param name="n"></param>
        /// <param name="m"></param>
        /// <param name="flag"></param>
        public static void Offset(ref float[][] matrix, int n, int m, bool inverse)
        {
            for (int i=0;i<n;i++)
                for (int j = 0; j < m; j++)
                {
                    int delta = -OffsetSize;
                    if (inverse) delta = OffsetSize;
                    matrix[i][j] += delta; 
                }
        }


        /// <summary>
        /// Quantilization
        /// </summary>
        /// <param name="matrix"></param>
        /// <param name="n"></param>
        /// <param name="m"></param>
        /// <param name="step"></param>
        /// <param name="result"></param>
        public static void Quantilization(ref float[][] matrix,int n,int m,int step, bool flag)
        {            
            for (int i = 0; i < n; i++)
            {         
                for (int j = 0; j < m; j++)
                {
                    if (flag)
                    {
                        matrix[i][j] = (short)(Math.Sign(matrix[i][j]) * ((Math.Abs(matrix[i][j]) / step)));
                    }
                    else
                    {
                        matrix[i][j] *= step;
                    }
                }
            }
        }

        /// <summary>
        /// Convert colors from RGB to ICT
        /// </summary>
        /// <param name="img"></param>
        /// <param name="Y"></param>
        /// <param name="Cb"></param>
        /// <param name="Cr"></param>
        public static void ICT(ref Bitmap img, out float[][] Y, out float[][] Cr, out float[][] Cb)
        {
            Y = new float[img.Height][];
            Cb = new float[img.Height][];
            Cr = new float[img.Height][];
            for (int i = 0; i < img.Height; i++)
            {
                Y[i] = new float[img.Width];
                Cb[i] = new float[img.Width];
                Cr[i] = new float[img.Width];
                for (int j = 0; j < img.Width; j++)
                {
                    Color col = img.GetPixel(i, j);
                    Y[i][j] = 0.299f * col.R + 0.586f * col.G + 0.114f * col.B;
                    Cr[i][j] = -0.169f * col.R - 0.331f * col.G + 0.5f * col.B;
                    Cb[i][j] = 0.5f * col.R - 0.419f * col.G -0.081f * col.B;
                }
            }
        }

        /// <summary>
        /// Convert colors from RGB to ICT
        /// </summary>
        /// <param name="Y"></param>
        /// <param name="Cr"></param>
        /// <param name="Cb"></param>
        public static void ICT(ref float[][] R,ref float[][]G,ref float[][] B, ref float[][] Y, ref float[][] Cr, ref float[][] Cb)
        {            
            for (int i = 0; i < R.Length; i++)
            {                
                for (int j = 0; j < R.Length; j++)
                {
                    //Color col = img.GetPixel(i, j);
                    Y[i][j] = 0.299f * R[i][j] + 0.586f * G[i][j] + 0.114f * B[i][j];
                    Cr[i][j] = -0.169f * R[i][j] - 0.331f * G[i][j] + 0.5f * B[i][j];
                    Cb[i][j] = 0.5f * R[i][j] - 0.419f * G[i][j] - 0.081f * B[i][j];
                }
            }
        }

        /// <summary>
        /// Convert colors from RGB to YUV
        /// </summary>
        /// <param name="img"></param>
        /// <param name="Y"></param>
        /// <param name="Cb"></param>
        /// <param name="Cr"></param>
        public static void YUV(ref Bitmap img, out float[][] Y, out float[][] U, out float[][] V)
        {
            Y = new float[img.Height][];
            U = new float[img.Height][];
            V = new float[img.Height][];
            for (int i = 0; i < img.Height; i++)
            {
                Y[i] = new float[img.Width];
                U[i] = new float[img.Width];
                V[i] = new float[img.Width];
                for (int j = 0; j < img.Width; j++)
                {
                    Color col = img.GetPixel(i, j);
                    Y[i][j] = (col.R + 2 * col.G + col.B)*0.25f;
                    U[i][j] = col.R - col.G;
                    V[i][j] = col.B - col.G;
                }
            }
        }

        /// <summary>
        /// Convert colors from RGB to YUV
        /// </summary>
        /// <param name="Y"></param>
        /// <param name="Cb"></param>
        /// <param name="Cr"></param>
        public static void YUV(ref float[][] R,ref float[][] G, ref float[][] B, ref float[][] Y, ref float[][] U, ref float[][] V)
        {
            for (int i = 0; i < R.Length; i++)
            {                
                for (int j = 0; j < R.Length; j++)
                {
                    //Color col = img.GetPixel(i, j);
                    Y[i][j] = (R[i][j] + 2 * G[i][j] + B[i][j]) * 0.25f;
                    U[i][j] = R[i][j] - G[i][j];
                    V[i][j] = B[i][j] - G[i][j];
                }
            }
        }

        /// <summary>
        /// Reconstruction RGB image from YUV
        /// </summary>
        /// <param name="Y"></param>
        /// <param name="U"></param>
        /// <param name="V"></param>
        /// <param name="img"></param>
        public static void RGBfromYUV(ref float[][] Y,ref float[][] U,ref float[][] V,out Bitmap img)
        {
            img = new Bitmap(Y.Length, Y[0].Length);
            for (int i = 0; i < img.Height; i++)
            {
                for (int j = 0; j < img.Width; j++)
                {
                    int G = (int)(Y[i][j]-((int)((U[i][j]+V[i][j])*0.25)));
                    int R = (int)(U[i][j] + G);
                    int B = (int)(V[i][j] + G);
                    if (G < 0) G = 0;
                    if (G > 255) G = 255;
                    if (R < 0) R = 0;
                    if (R > 255) R = 255;
                    if (B < 0) B = 0;
                    if (B > 255) B = 255;
                    img.SetPixel(i, j, Color.FromArgb(R, G, B));
                }
            }
        }

        /// <summary>
        /// RGB from YUV
        /// </summary>
        /// <param name="Y"></param>
        /// <param name="U"></param>
        /// <param name="V"></param>
        /// <param name="R"></param>
        /// <param name="G"></param>
        /// <param name="B"></param>
        public static void RGBfromYUV(ref float[][] Y, ref float[][] U, ref float[][] V,ref float[][] R,ref float[][] G,ref float[][] B)
        {            
            for (int i = 0; i < Y.Length; i++)
            {
                for (int j = 0; j < Y.Length; j++)
                {
                    int g = (int)(Y[i][j] - ((int)((U[i][j] + V[i][j]) * 0.25)));
                    int r = (int)(U[i][j] + g);
                    int b = (int)(V[i][j] + g);
                    if (g < 0) g = 0;
                    if (g > 255) g = 255;
                    if (r < 0) r = 0;
                    if (r > 255) r = 255;
                    if (b < 0) b = 0;
                    if (b > 255) b = 255;
                    R[i][j] = r;
                    G[i][j] = g;
                    B[i][j] = b;
                }
            }

        }

        /// <summary>
        /// Reconstruction RGB image from ICT
        /// </summary>
        /// <param name="Y"></param>
        /// <param name="Cb"></param>
        /// <param name="Cr"></param>
        /// <param name="img"></param>
        public static void RGBfromICT(ref float[][] Y,ref float[][] Cr,ref float[][] Cb,out Bitmap img)
        {
            img = new Bitmap(Y.Length, Y[0].Length);
            for (int i = 0; i < img.Height; i++)
            {               
                for (int j = 0; j < img.Width; j++)
                {
                    int R = (int)(Y[i][j] + 1.4021 * Cb[i][j]);
                    if (R < 0) R = 0;
                    if (R > 255) R = 255;
                    int G = (int)(Y[i][j] -0.3441*Cr[i][j] - 0.7142 * Cb[i][j]);
                    if (G < 0) G = 0;
                    if (G > 255) G = 255;
                    int B = (int)(Y[i][j] +1.7718 * Cr[i][j]);
                    if (B < 0) B = 0;
                    if (B > 255) B = 255;
                    img.SetPixel(i,j,Color.FromArgb(R,G,B));
                }
            }
        }

        /// <summary>
        /// Reconstruction RGB image from ICT
        /// </summary>
        /// <param name="Y"></param>
        /// <param name="Cr"></param>
        /// <param name="Cb"></param>
        /// <param name="R"></param>
        /// <param name="G"></param>
        /// <param name="B"></param>
        public static void RGBfromICT(ref float[][] Y, ref float[][] Cr, ref float[][] Cb, ref float[][] R, ref float[][] G, ref float[][] B)
        {            
            for (int i = 0; i < Y.Length; i++)
            {
                for (int j = 0; j < Y.Length; j++)
                {
                    int r = (int)(Y[i][j] + 1.4021 * Cb[i][j]);
                    if (r < 0) r = 0;
                    if (r > 255) r = 255;
                    int g = (int)(Y[i][j] - 0.3441 * Cr[i][j] - 0.7142 * Cb[i][j]);
                    if (g < 0) g = 0;
                    if (g > 255) g = 255;
                    int b = (int)(Y[i][j] + 1.7718 * Cr[i][j]);
                    if (b < 0) b = 0;
                    if (b > 255) b = 255;
                    R[i][j] = r;
                    G[i][j] = g;
                    B[i][j] = b;
                }
            }
        }
        /// <summary>
        /// Copy matrix
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="size"></param>
        public static void Move(ref float[][] a, ref float[][] b, int size)
        {
            for (int i = 0; i < size; i++)
                for (int j = 0; j < size; j++)
                    b[i][j] = a[i][j];
        }

        /// <summary>
        /// Write Packet into file
        /// </summary>
        /// <param name="size"></param>
        /// <param name="A"></param>
        /// <param name="B"></param>
        /// <param name="C"></param>
        /// <param name="worker"></param>
        public static void SavePackets(int size, float[][] A, float[][] B, float[][] C, FileWorker worker)
        {
            ImagePacket pack = new ImagePacket();
            UInt32 cnt = 0;
            Int16 val = (short)A[0][0];
            for (ushort ii = 0; ii < size; ii++)
                for (ushort jj = 0; jj < size; jj++)
                {
                    Int16 cur = (short)A[ii][jj];
                    if (cur == val)
                    {
                        cnt++;
                    }
                    else
                    {
                        pack.FirstCollection.Add(new WaveletPoint(cnt, val));
                        val = cur;
                        cnt = 1;
                    }
                }
            pack.FirstCollection.Add(new WaveletPoint(cnt, val));
            cnt = 0;
            val = (short)B[0][0];
            for (ushort ii = 0; ii < size; ii++)
                for (ushort jj = 0; jj < size; jj++)
                {
                    Int16 cur = (short)B[ii][jj];
                    if (cur == val)
                    {
                        cnt++;
                    }
                    else
                    {
                        pack.SecondCollection.Add(new WaveletPoint(cnt, val));
                        val = cur;
                        cnt = 1;
                    }
                }
            pack.SecondCollection.Add(new WaveletPoint(cnt, val));
            cnt = 0;
            val = (short)C[0][0];
            for (ushort ii = 0; ii < size; ii++)
                for (ushort jj = 0; jj < size; jj++)
                {
                    Int16 cur = (short)C[ii][jj];
                    if (cur == val)
                    {
                        cnt++;
                    }
                    else
                    {
                        pack.ThridCollection.Add(new WaveletPoint(cnt, val));
                        val = cur;
                        cnt = 1;
                    }
                }
            pack.ThridCollection.Add(new WaveletPoint(cnt, val));

            pack.SavePacket(worker);
        }

        /// <summary>
        /// Load Matrix from packet
        /// </summary>
        /// <param name="pack"></param>
        /// <param name="A"></param>
        /// <param name="B"></param>
        /// <param name="C"></param>
        /// <param name="size"></param>
        public static void LoadMatrixFromPacket(ImagePacket pack, ref float[][] A, ref float[][] B, ref float[][] C, int size)
        {
            int i = 0;
            int j = 0;
            for (int k = 0; k < pack.FirstCollection.Count; k++)
            {
                for (int l = 0; l < pack.FirstCollection[k].Cnt; l++)
                {
                    A[i][j] = pack.FirstCollection[k].Value;
                    j++;
                    if (j >= size)
                    {
                        j = 0;
                        i++;
                    }
                }
            }
            i = 0;
            j = 0;
            for (int k = 0; k < pack.SecondCollection.Count; k++)
            {
                for (int l = 0; l < pack.SecondCollection[k].Cnt; l++)
                {
                    B[i][j] = pack.SecondCollection[k].Value;
                    j++;
                    if (j >= size)
                    {
                        j = 0;
                        i++;
                    }
                }
            }
            i = 0;
            j = 0;
            for (int k = 0; k < pack.ThridCollection.Count; k++)
            {
                for (int l = 0; l < pack.ThridCollection[k].Cnt; l++)
                {
                    C[i][j] = pack.ThridCollection[k].Value;
                    j++;
                    if (j >= size)
                    {
                        j = 0;
                        i++;
                    }
                }
            }
        }
    }
}
